package com.xxwy.groupchat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

/**
 * @author wy
 * @date 2020/3/6 9:51
 */
public class ChatServerProvider {

  private int port;

  public ChatServerProvider(int port) {
    this.port = port;
  }

  public void start() {
    NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
    NioEventLoopGroup workerGroup = new NioEventLoopGroup();

    ServerBootstrap serverBootstrap = new ServerBootstrap();

    serverBootstrap.group(bossGroup,workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG,128)
        .childOption(ChannelOption.SO_KEEPALIVE,true)
        .handler(new LoggingHandler(LogLevel.INFO))
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
          @Override
          protected void initChannel(NioSocketChannel socketChannel) throws Exception {
            socketChannel.pipeline().addLast(new StringDecoder());
            socketChannel.pipeline().addLast(new StringEncoder());
            socketChannel.pipeline().addLast(new ServerHandler());

            /**
             * 空闲状态处理器，
             * readerIdleTime 表示多长时间没有读 就会发送一些心跳检检测是否连接
             * writerIdleTime 表示多长时间没有写 就会发送一些心跳检检测是否连接
             * allIdleTime 表示多长时间没有读和写，就会发送一些心跳检检测是否连接
             * 当设置时间没有读和写或读写， 触发一个事件IdleStateEvent
             *
             * 为什么有关闭响应事件还要有心跳检测，因为有的情况下，关闭情况无法感知
             *
             * IdleStateEvent被触发，会触发pipeline下一个handle的userEventTriggered()去处理
             */
            socketChannel.pipeline().addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));
            //加一个队空闲检测进一步处理的handler(自定义)
            socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() {

              /**
               * 处理，读，写，读写空闲
               * @param ctx
               * @param evt
               * @throws Exception
               */
              @Override
              public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                  if(evt instanceof IdleStateEvent){
                    IdleStateEvent stateEvent = (IdleStateEvent) evt;
                    String eventType = null;
                    switch (stateEvent.state()){
                      case WRITER_IDLE:
                        eventType = "写空闲";
                        break;
                      case READER_IDLE:
                        eventType = "读空闲";
                        break;
                      case ALL_IDLE:
                        eventType = "读写空闲";
                        break;
                    }
                    System.out.println(ctx.channel().remoteAddress() +"--超时--" + eventType);
                  }
              }
            });
          }
        });

    try {
      ChannelFuture sync = serverBootstrap.bind(port).sync();
      sync.channel().closeFuture().sync();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }finally {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }
  }
}
